package marioarena.game;
import java.awt.Point;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Random;

import marioarena.ConnectionHandler;
import marioarena.Settings;


public class Level {
	private static Level instance = null;
	private int[][] tileGrid;
	private int levelWidth;
	private int levelHeight;
	private ArrayList<Point> spawnPoints;
	private Random spawnRandomGenerator;
	
	//SINGLETON
	private Level(){
	}
	public static Level getInstance() {
		if(instance == null) {
			instance = new Level();
		}
		return instance;
	}

	/**
	 * Loads a level, sending it to all connected clients.
	 */
	public void loadLevel(){
		System.out.println("Loading level...");
		File file = new File(System.getProperty("user.dir") + "/" + Settings.selectedLevel);
		try{
			// Open the file that is the first 
			// command line parameter
			FileInputStream fstream = new FileInputStream(file);
			// Get the object of DataInputStream
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			
			String line;
			String fullLevelString = "";
			//Read all lines from file, which should be just one.
			while ((line = br.readLine()) != null)   {
				fullLevelString += line;
			}
			//Close the input stream
			in.close();
			createTileGrid(fullLevelString);
			shareLevelWithClients(fullLevelString);
			
		}catch (Exception e){//Catch exception if any
			System.out.println("Error: " + e.getMessage());
		}
	}
	
	/**
	 * Processes the level, storing it in the levelGrid so the server can make calculations with its tiles.
	 */
	private void createTileGrid(String level){
		this.spawnPoints = new ArrayList<Point>();
		this.spawnRandomGenerator = new Random();
		//retrieve level settings
		String[] splitOnColon = level.split(":"); 
		this.levelWidth = Integer.parseInt(splitOnColon[0]);
		this.levelHeight = Integer.parseInt(splitOnColon[1]);
		//retrieve leveldata
		String[] tilePlacements = splitOnColon[2].split("\\|");
		int amountOfTilePlacements = tilePlacements.length;
		
		//Initialize the twodimensional array containing all tiles
		this.tileGrid = new int[levelWidth][levelHeight];
		
		for(int i = 0; i < amountOfTilePlacements; i++){
			//retrieve data of the set of tiles
			String[] values = tilePlacements[i].split("#");
			int startIndexX = Integer.parseInt(values[0]);
	        int startIndexY = Integer.parseInt(values[1]);
	        int endIndexX = Integer.parseInt(values[2]);
	        int endIndexY = Integer.parseInt(values[3]);
	        int tileNumber = Integer.parseInt(values[4]);
	        
	        //place the set of tiles in the level grid.
	        for (int y = startIndexY; y <= endIndexY; y++)
	        {
	            for (int x = startIndexX; x <= endIndexX; x++)
	            {
	                this.tileGrid[x][y] = tileNumber;
	                if(tileNumber == 100) //Store all spawnpoints
	                	this.spawnPoints.add(new Point(x, y));
	            }
	        }
		}
		System.out.println("Found " + this.spawnPoints.size() + " spawnpoint(s).");
	}
	
	/**
	 * Returns a tile from the level/tilegrid that is positioned at the given coordinates
	 * @param xIndex X index
	 * @param yIndex Y index
	 * @return Returns the tilenumber
	 */
	public int getTile(int xIndex, int yIndex){
		//Check if index can exist (cannot be smaller than 0, and must be smaller than width/height)
		if(xIndex >= 0 && xIndex < this.levelWidth && yIndex >= 0 && yIndex < this.levelHeight)
			return this.tileGrid[xIndex][yIndex];
		else{
			return 0; //outside the level
		}
	}
	
	/**
	 * Generates a random spawnpoint.
	 * @return A random spawnpoint.
	 */
	public Point getRandomSpawnPoint(){
		int index = spawnRandomGenerator.nextInt(this.spawnPoints.size());
		return this.spawnPoints.get(index);
	}
	
	/**
	 * Share a level with all connected clients using TCP.
	 * @param levelData The level string to send.
	 */
	private void shareLevelWithClients(String levelData){
		System.out.println("Level Loaded.\nTransferring level to clients.");
		ConnectionHandler.getInstance().sendToAllClients("6_" + levelData);
	}
	
	public int getWidthInTiles(){
		return this.levelWidth;
	}
	
	public int getHeightInTiles(){
		return this.levelHeight;
	}
}
